package org.shell;

import java.io.File;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.shell.commands.CommandBuilderImpl;
import org.shell.commands.CommandInvoker;
import org.shell.commands.CommandsHolder;
import org.shell.commands.InputCommand;
import org.shell.commands.ShellCommand;
import org.shell.loaders.JarLoader;
import org.shell.loaders.Loader;
import org.shell.loaders.exceptions.LoaderGeneralException;
import org.shell.utils.config.ConfigManager;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;

public class ConfigLoader {

    public ConfigLoader(String configPath) throws ConfigFileNotFoundException, GeneralXMLException {
        if (!new File(configPath).exists()) {
            throw new ConfigFileNotFoundException(configPath);
        } else {
            try {
                parseXML(configPath);
            } catch (Exception e) {
                throw new GeneralXMLException(e.getMessage());
            }
        }
    }

    private void parseXML(String configPath) throws Exception {
        DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document document = db.parse(configPath);
        Element config = document.getDocumentElement();
        if (!config.getNodeName().equals("config")) {
            throw new GeneralXMLException("root element not found, expected 'config'");
        } else {
            parseConfig(config);
        }
    }

    private void parseConfig(Element config) throws Exception {
        for (Node childNode = config.getFirstChild(); childNode != null; childNode = childNode.getNextSibling()) {
            if (childNode instanceof Element) {
                Element childElement = (Element) childNode;
                if (childElement.getNodeName().equals("jars")) {
                    parseJars(childElement);
                } else if (childElement.getNodeName().equals("properties")) {
                    parseProperties(childElement);
                } else if (childElement.getNodeName().equals("invocations")) {
                    parseInvokations(childElement);
                } else {
                    throw new GeneralXMLException("wrong node name, expected 'jars'|'properties'|'invocations'");
                }
            }
        }
    }

    private void parseJars(Element jars) throws LoaderGeneralException, GeneralXMLException {
        for (Node childNode = jars.getFirstChild(); childNode != null; childNode = childNode.getNextSibling()) {
            if (childNode instanceof Element) {
                Element childElement = (Element) childNode;
                if (childElement.getNodeName().equals("file")) {
                    Loader jarLoader = new JarLoader();
                    jarLoader.load(childElement.getTextContent().trim(), Boolean.FALSE);
                } else {
                    throw new GeneralXMLException("wrong node name, expected 'file'");
                }
            }
        }
    }

    private void parseProperties(Element properties) throws GeneralXMLException {
        for (Node childNode = properties.getFirstChild(); childNode != null; childNode = childNode.getNextSibling()) {
            if (childNode instanceof Element) {
                Element childElement = (Element) childNode;
                if (childElement.getNodeName().equals("property")) {
                    if (childElement.hasAttribute("name") && childElement.hasAttribute("value")) {
                        String name = childElement.getAttribute("name");
                        String value = childElement.getAttribute("value");
                        ConfigManager.set(name, value);
                    } else {
                        throw new GeneralXMLException("tag 'property' has to have 'name' and 'value' attributes");
                    }
                }
            }
        }
    }

    private void parseInvokations(Element invocations) throws Exception {
        for (Node childNode = invocations.getFirstChild(); childNode != null; childNode = childNode.getNextSibling()) {
            if (childNode instanceof Element) {
                Element childElement = (Element) childNode;
                if (childElement.getNodeName().equals("invoke")) {
                    Shell.handleInput(childElement.getTextContent().trim());
                } else {
                    throw new GeneralXMLException("wrong node name, expected 'invoke'");
                }
            }
        }
    }
}
